from typing import NamedTuple, List, Dict, Tuple
import math
import csv
import json
from pathlib import Path


class UTMCoordinate(NamedTuple):
    """UTM坐标数据结构"""
    easting: float  # 东坐标
    northing: float  # 北坐标
    zone_number: int  # 区域编号
    zone_letter: str  # 区域字母
    latitude: float  # 原始纬度
    longitude: float  # 原始经度


def wgs84_to_utm6(lat: float, lon: float) -> UTMCoordinate:
    """
    将WGS84经纬度坐标转换为UTM6坐标系

    参数:
        lat: 纬度 (degrees)
        lon: 经度 (degrees)

    返回:
        UTMCoordinate: UTM坐标对象
    """
    # UTM参数
    a = 6378137.0  # 地球长半轴 (WGS84)
    f = 1 / 298.257223563  # 扁率 (WGS84)
    k0 = 0.9996  # 比例因子
    false_easting = 500000.0  # 假东偏移
    false_northing = 0.0  # 假北偏移 (北半球)

    # 确定UTM区域 (固定为6度带)
    zone_number = int((lon + 180) / 6) + 1

    # 确定区域字母
    if lat >= 84.0:
        zone_letter = 'Z'
    elif lat >= 72.0:
        zone_letter = 'X'
    elif lat >= 64.0:
        zone_letter = 'W'
    elif lat >= 56.0:
        zone_letter = 'V'
    elif lat >= 48.0:
        zone_letter = 'U'
    elif lat >= 40.0:
        zone_letter = 'T'
    elif lat >= 32.0:
        zone_letter = 'S'
    elif lat >= 24.0:
        zone_letter = 'R'
    elif lat >= 16.0:
        zone_letter = 'Q'
    elif lat >= 8.0:
        zone_letter = 'P'
    elif lat >= 0.0:
        zone_letter = 'N'
    elif lat >= -8.0:
        zone_letter = 'M'
    elif lat >= -16.0:
        zone_letter = 'L'
    elif lat >= -24.0:
        zone_letter = 'K'
    elif lat >= -32.0:
        zone_letter = 'J'
    elif lat >= -40.0:
        zone_letter = 'H'
    elif lat >= -48.0:
        zone_letter = 'G'
    elif lat >= -56.0:
        zone_letter = 'F'
    elif lat >= -64.0:
        zone_letter = 'E'
    elif lat >= -72.0:
        zone_letter = 'D'
    else:
        zone_letter = 'C'

    # 将经纬度转换为弧度
    lat_rad = math.radians(lat)
    lon_rad = math.radians(lon)

    # 计算中央子午线经度 (弧度)
    lon_origin = math.radians((zone_number - 1) * 6 - 180 + 3)

    # 计算经度差
    d_lon = lon_rad - lon_origin

    # 计算辅助参数
    e2 = 2 * f - f * f  # 第一偏心率的平方
    e_prime2 = e2 / (1 - e2)  # 第二偏心率的平方
    n = a / math.sqrt(1 - e2 * math.sin(lat_rad) * math.sin(lat_rad))
    t = math.tan(lat_rad) * math.tan(lat_rad)
    c = e_prime2 * math.cos(lat_rad) * math.cos(lat_rad)
    a0 = 1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256
    a2 = 3 * (e2 + e2 * e2 / 4 + 15 * e2 * e2 * e2 / 128) / 8
    a4 = 15 * (e2 * e2 + 3 * e2 * e2 * e2 / 4) / 256
    a6 = 35 * e2 * e2 * e2 / 3072
    m = a * (a0 * lat_rad - a2 * math.sin(2 * lat_rad) + a4 * math.sin(4 * lat_rad) - a6 * math.sin(6 * lat_rad))

    # 计算东坐标和北坐标
    easting = k0 * n * (d_lon + (1 - t + c) * d_lon * d_lon * d_lon / 6 +
                        (5 - 18 * t + t * t + 72 * c - 58 * e_prime2) *
                        d_lon * d_lon * d_lon * d_lon * d_lon / 120) + false_easting

    northing = k0 * (m + n * math.tan(lat_rad) *
                     (d_lon * d_lon / 2 + (5 - t + 9 * c + 4 * c * c) *
                      d_lon * d_lon * d_lon * d_lon / 24 +
                      (61 - 58 * t + t * t + 600 * c - 330 * e_prime2) *
                      d_lon * d_lon * d_lon * d_lon * d_lon * d_lon / 720))

    # 南半球调整
    if lat < 0:
        northing += 10000000.0  # 南半球假北偏移

    return UTMCoordinate(
        easting=easting,
        northing=northing,
        zone_number=zone_number,
        zone_letter=zone_letter,
        latitude=lat,
        longitude=lon
    )


def convert_gps_to_utm6(input_file: Path, output_format: str = "csv"):
    """
    将GPS坐标文件转换为UTM6坐标

    参数:
        input_file: 输入的GPS坐标文件 (CSV或JSON)
        output_format: 输出格式 ("csv" 或 "json")
    """
    # 确定输入文件类型并读取数据
    coordinates = []
    if input_file.suffix.lower() == '.csv':
        with open(input_file, 'r', encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                if '纬度' in row and '经度' in row:
                    coordinates.append({
                        'file': row.get('文件名', ''),
                        'latitude': float(row['纬度']),
                        'longitude': float(row['经度']),
                        'altitude': float(row.get('海拔(m)', 0)),
                        'timestamp': row.get('拍摄时间', '')
                    })
    elif input_file.suffix.lower() == '.json':
        with open(input_file, 'r', encoding='utf-8') as f:
            coordinates = json.load(f)
    else:
        raise ValueError("不支持的输入文件格式。支持CSV和JSON格式。")

    # 转换坐标
    utm_results = []
    for coord in coordinates:
        utm_coord = wgs84_to_utm6(coord['latitude'], coord['longitude'])
        utm_results.append({
            'file': coord['file'],
            'easting': utm_coord.easting,
            'northing': utm_coord.northing,
            'zone_number': utm_coord.zone_number,
            'zone_letter': utm_coord.zone_letter,
            'original_latitude': coord['latitude'],
            'original_longitude': coord['longitude'],
            'altitude': coord.get('altitude', 0),
            'timestamp': coord.get('timestamp', '')
        })

    # 输出结果
    output_file = input_file.parent / f"utm6_coordinates{input_file.suffix}"
    if output_format.lower() == "csv":
        with open(output_file, 'w', newline='', encoding='utf-8') as f:
            fieldnames = ['文件名', '东坐标', '北坐标', '区域编号', '区域字母',
                          '原始纬度', '原始经度', '海拔(m)', '拍摄时间']
            writer = csv.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()
            for result in utm_results:
                writer.writerow({
                    '文件名': result['file'],
                    '东坐标': result['easting'],
                    '北坐标': result['northing'],
                    '区域编号': result['zone_number'],
                    '区域字母': result['zone_letter'],
                    '原始纬度': result['original_latitude'],
                    '原始经度': result['original_longitude'],
                    '海拔(m)': result['altitude'],
                    '拍摄时间': result['timestamp']
                })
    else:  # JSON格式
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(utm_results, f, indent=2, ensure_ascii=False)

    print(f"转换完成! 结果已保存到: {output_file}")


def batch_convert_directory(directory: Path, output_format: str = "csv"):
    """
    批量转换目录中的所有GPS坐标文件

    参数:
        directory: 包含GPS坐标文件的目录
        output_format: 输出格式 ("csv" 或 "json")
    """
    # 查找目录中的GPS坐标文件
    gps_files = list(directory.glob('gps_coordinates.csv')) + list(directory.glob('gps_coordinates.json'))

    if not gps_files:
        print("未找到GPS坐标文件。请先运行GPS提取程序。")
        return

    for gps_file in gps_files:
        print(f"处理文件: {gps_file.name}")
        convert_gps_to_utm6(gps_file, output_format)


if __name__ == "__main__":
    # 使用示例
    data_dir = Path("test_images")  # 替换为您的数据目录

    # 方式1: 直接转换单个文件
    # gps_file = data_dir / "gps_coordinates.csv"
    # convert_gps_to_utm6(gps_file, "csv")

    # 方式2: 批量处理目录中的所有GPS文件
    batch_convert_directory(data_dir, "csv")